#pragma once

#include "formatter.hpp"
#include <fstream>
#include <time.h>
namespace Log
{
    class LogSink
    {
    public:
        using ptr = std::shared_ptr<LogSink>;
        virtual ~LogSink() {}
        virtual void log(const char *data, size_t len) = 0;
    };

    class StdOutSink : public LogSink
    {
    public:
        void log(const char *data, size_t len) override
        {
            // 这里不能使用 cout <<  因为它是通过 '\0'判断结尾的
            std::cout.write(data, len);
        }
    };

    class FileSink : public LogSink
    {
    public:
        FileSink(const std::string &filename)
            : _filename(filename)
        {
            // 创建文件所在的路径上的文件夹
            util::file::create_directory(util::file::path(_filename));
            // 文件操作句柄统一管理起来
            _ofs.open(_filename, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        void log(const char *data, size_t len) override
        {
            _ofs.write(data, len);
            if (_ofs.good() == false) // good函数用于检测流对象的当前状态是否“良好”，即没有发生任何错误。
            {
                std::cout << "⽇志输出⽂件失败！\n";
            }
        }

    private:
        std::string _filename; // 文件的名字
        std::ofstream _ofs;    // 文件输出流
    };

    class RollBySizeSink : public LogSink
    {
    public:
        RollBySizeSink(const std::string &basename, size_t max_fsize)
            : _basename(basename),
              _max_fsize(max_fsize),
              _cur_fsize(0)
        {
            std::string filename = create_file();
            // 创建文件所在的路径上的文件夹
            util::file::create_directory(util::file::path(filename));
            // 文件操作句柄统一管理起来
            _ofs.open(filename, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        void log(const char *data, size_t len) override
        {
            initLogFile();
            _ofs.write(data, len);
            if (_ofs.good() == false) // good函数用于检测流对象的当前状态是否“良好”，即没有发生任何错误。
            {
                std::cout << "⽇志输出⽂件失败！\n";
            }
            _cur_fsize += len;
        }

    private:
        void initLogFile() // 如果文件超出大小则需要创造文件进行写入
        {
            if (_cur_fsize >= _max_fsize)
            {
                _ofs.close();
                // 创建文件所在的路径上的文件夹
                std::string filename = create_file();
                util::file::create_directory(util::file::path(filename));
                // 文件操作句柄统一管理起来
                _ofs.open(filename, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
                _cur_fsize = 0;
            }
        }

        std::string create_file()
        {
            /*
            当日志数据在短时间内迅速增长，
            文件大小达到了设定的最大值（例如，一秒钟内），
            文件输出流 (_ofs) 可能没有及时关闭并创建一个新的日志文件，
            这导致后续的数据仍然写入同一个文件，而没有按预期进行文件切割
            */
            static time_t last_time = 0;
            static int counter = 0;

            time_t t = util::date::now();
            struct tm lt;
            localtime_r(&t, &lt);
            if (t != last_time)
            {
                // 时间变化，重置计数器
                last_time = t;
                counter = 0;
            }
            std::stringstream ss;
            ss << _basename;
            ss << lt.tm_year + 1900;
            ss << lt.tm_mon + 1;
            ss << lt.tm_mday;
            ss << lt.tm_hour;
            ss << lt.tm_min;
            ss << lt.tm_sec;
            ss << "_" << counter++; // 加入自增编号
            ss << ".log";
            return ss.str();
        }

    private:
        size_t _max_fsize;     // 用户自定义规定的 文件大小
        size_t _cur_fsize;     // 当前文件的大小
        std::string _basename; // 文件基础名字
        std::ofstream _ofs;    // 文件输出流
    };

    /* 扩展实现一个rollbytime */
    class RollByTimeSink : public LogSink
    {
    public:
        enum class GapTime
        {
            Second = 1,
            Minute = 60,
            Hour = 3600,
            Day = 3600 * 24
        };

    public:
        RollByTimeSink(const std::string &basename, GapTime gap_type)
            : _basename(basename),
              _gap_type(static_cast<size_t>(gap_type))
        {
            _cur_gap = _cur_gap == 1 ? util::date::now() : util::date::now() % _gap_type; /*当间隔为second时，%任何东西都是0*/
            std::string filename = create_file();
            // 创建文件所在的路径上的文件夹
            util::file::create_directory(util::file::path(filename));
            // 文件操作句柄统一管理起来
            _ofs.open(filename, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }

        void log(const char *data, size_t len) override
        {
            initLogFile();
            _ofs.write(data, len);
            if (_ofs.good() == false) // good函数用于检测流对象的当前状态是否“良好”，即没有发生任何错误。
            {
                std::cout << "⽇志输出⽂件失败！\n";
            }
        }

    private:
        void initLogFile() // 如果文件超出大小则需要创造文件进行写入
        {
            size_t new_gap = _gap_type == 1 ? util::date::now() : util::date::now() % _gap_type; /*当间隔为second时，%任何东西都是0*/
            if (new_gap != _cur_gap)
            {
                _ofs.close();
                // 创建文件所在的路径上的文件夹
                std::string filename = create_file();
                util::file::create_directory(util::file::path(filename));
                // 文件操作句柄统一管理起来
                _ofs.open(filename, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
                _cur_gap = new_gap;
            }
        }

        std::string create_file()
        {
            time_t t = util::date::now();
            struct tm lt;
            localtime_r(&t, &lt);

            std::stringstream ss;
            ss << _basename;
            ss << lt.tm_year + 1900;
            ss << lt.tm_mon + 1;
            ss << lt.tm_mday;
            ss << lt.tm_hour;
            ss << lt.tm_min;
            ss << lt.tm_sec;
            ss << ".log";
            return ss.str();
        }

    private:
        size_t _gap_type;      // 间隔时间
        size_t _cur_gap;       // 当前的 时间%间隔时间
        std::string _basename; // 文件基础名字
        std::ofstream _ofs;    // 文件输出流
    };

    class SinkFactory
    {
    public:
        template <class SinkType, class... Args>
        static LogSink::ptr create(Args &&...args)
        {
            /*  Args&&... —— 这是一种 万能引用（universal reference）,它可以绑定到左值或右值，并保持原来的值类别。
                如果你直接写 args...，所有参数都被当作 左值 传递
                而 std::forward<Args>(args)... 可以让：
                左值参数以左值身份传递；
                右值参数以右值身份传递。*/
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };
}
